嵌入式系統、硬體等常會用到位元遮罩(bitmasking),讓我們得以調整指定位元內容而不影響其他位元,這篇文章就來看要如何透過 &、|、^、~、<< 互相搭配達成。
以 0b 為開頭(b 為 binary 代表二進制),後面接上這個數字的每個位元,最右邊的位元為第 0 個,稱為「最不重要位元(Least Significant Bit, LSB)」,依序往左為第 1、2、3、... 個位元,直到最左邊的稱為「最重要位元(Most Significant Bit, MSB)」。
例如十進位的 1 在二進位中是 0b 0000 0001,其中除第 0 個位元 LSB 為 1 外,其餘位元皆為 0。

除了在二進制中是 0b 0000 0001 的 1,我們再假設有兩個數字:
a 為 0b 1011 1000(第三位元為 1)b 為 0b 1011 0000(第三位元為 0)&這個 & 要分兩種情形來看,可以用來把指定位元設成 0,或者查看指定位元內容。
& 把指定位元設成 0寫法如下,留意括弧前有 ~ 符號,也就是進行 & 運算前要先反轉每個位元:
a &= ~(1 << 3);
或
b &= ~(1 << 3);
流程為:
(1 << 3) 把 1(也就是 0b 0000 0001)往左移 3 位元,變成 0b 0000 1000。~ 反轉位元,把 0b 0000 1000 變成 0b 1111 0111。&=,相當於 a &= 0b 1111 0111 如下(每個位元上下比較 1&1 = 1、1&0 = 0、0&1 = 0、0&0 = 0):a = 0b 1011 1000
& 0b 1111 0111
----------------
0b 1011 0000 // a 的新值
或者 b & 0b 1111 0111 如下:
b = 0b 1011 0000
& 0b 1111 0111
----------------
0b 1011 0000 // b 的新值
由上述分析,得:
1 時,位元變為 0。0 時,位元仍為 0。由此可知 & 搭配 ~ 的功能為把指定位元變成 0。
& 查看指定位元內容寫法如下,留意括弧前沒有 ~ 符號、& 後面沒有 =:
a & (1 << 3);
或
b & (1 << 3);
流程如下,沒有反轉位元過程:
(1 << 3) 把 1(也就是 0b 0000 0001)往左移 3 位元,變成 0b 0000 1000。&,這裡沒有 = 是因為沒有要改變 a 的值,所以不須要把運算結果透過 = 賦值回去 a,相當於直接做 a & 0b 0000 1000 如下(每個位元上下比較 1&1 = 1、1&0 = 0、0&1 = 0、0&0 = 0):a = 0b 1011 1000
& 0b 0000 1000
----------------
0b 0000 1000 // 不拿這個值去改變 a
而如果是操作 b & 0b 0000 1000 則如下,一樣沒有 = 把新產生的值賦值回 b:
b = 0b 1011 0000
& 0b 0000 1000
----------------
0b 0000 1000 // 不拿這個值去改變 b
由上述分析,得:
1 時,位元依然是 1。0 時,位元依然是 0。由此可知 & 但不帶 ~ 時是單純查看指定位元內容,而查看時不用改變數值,因此不用 = 賦值回去原數字。
|用來把指定位元設成 1,例如:
a |= (1 << 3);
或
b |= (1 << 3);
流程為:
(1 << 3) 把 1(也就是 0b 0000 0001)往左移 3 位元,變成 0b 0000 1000。|=,相當於 a |= 0b 1111 0111 如下(每個位元上下比較 1|1 = 1、1|0 = 1、0|1 = 1、0|0 = 0):a = 0b 1011 1000
| 0b 0000 1000
----------------
0b 1011 1000 // a 的新值
或者 b | 0b 1111 0111 如下:
b = 0b 1011 0000
| 0b 0000 1000
----------------
0b 1011 1000 // b 的新值
由上述分析,得:
1 時,位元仍為 1。0 時,位元變為 1。由此可知 | 的功能是把指定位元變成 1。
^用來反轉指定位元,例如:
a ^= (1 << 3);
或
b ^= (1 << 3);
流程為:
(1 << 3) 把 1(也就是 0b 0000 0001)往左移 3 位元,變成 0b 0000 1000。^=,相當於 a ^= 0b 0000 1000 如下(每個位元上下比較 1^1 = 0、1^0 = 1、0^1 = 1、0^0 = 0):a = 0b 1011 1000
^ 0b 0000 1000
----------------
0b 1011 0000 // a 的新值
或者 b ^ 0b 0000 1000 如下:
b = 0b 1011 0000
^ 0b 0000 1000
----------------
0b 1011 1000 // b 的新值
由上述分析,得:
1 時,位元變為 0。0 時,位元變為 1。由此可知 | 的功能是把指定位元反轉。
x &= ~(1 << n):把 x 的第 n 個位元強制改成 0。x & (1 << n):查看 x 的第 n 個位元內容。x |= (1 << n):把 x 的第 n 個位元強制改成 1。x ^= (1 << n):把 x 的第 n 個位元反轉。可以用巨集表示如下:
#define CLEAR_BIT(p,n) (p &= ~(1 << n))
#define CHECK_BIT(p,n) (p & (1 << n)
#define SET_BIT(p,n) (p |= (1 << n))
#define FLIP_BIT(p,n) (p ^= (1 << n))
或者先把左移寫到巨集、再用函數操作位元遮罩如下:
#define BIT3 (0x1 << 3) // 先在巨集定義左移三位元
// 表示要對後續變數 a 的第三個位元進行操作
static int a;
// 再於下列函數對變數 a 做位元遮罩
void clear_bit3(void) {
a &= ~BIT3;
}
void check_bit3(void) {
a & BIT3;
}
void set_bit3(void) {
a |= BIT3;
}
void flip_bit3(void) {
a ^= BIT3;
}